home *** CD-ROM | disk | FTP | other *** search
- /*
- This file contains functions that support undo and redo actions
- for a single text editor (window).
-
-
- This file is for POV-Ray 3.0.1 for MacOS!
- The POV-Ray-Team is allowed to use this source code as they like
- to use it.
-
- Created by Thorsten Froehlich, 1997.
- */
-
- /*
- This file will ONLY work with the TE32K text editor!
- ====================================================
-
- This is a standard C undo/redo system. If you use C++ and
- a text editor class, these variables and functions should
- be members of the class. Then each text editor (window)
- will have its own undo/redo. (My original version is C++.)
- With this standard C source code you can only support ONE
- text editor (window) without modifications.
-
- Use gUndoBufferCount and gRedoBufferCount to enable/disable the
- "Undo" and "Redo" menu items, e.g. if the counter is > 0 enable
- the menu item, if it is == 0 disable it.
-
- How it works:
- -------------
- Place the Begin_Make_Undoable and End_Make_Undoable around your
- code that changes the text (e.g. Cut,Paste...).
- To make sure it works you have to set the selection range as it
- is required. E.g. if you perform a Cut operation make sure the
- text that is cut out is selected BEFORE you call
- Begin_Make_Undoable and after the text is cut out make sure
- there is no (an empty) selection BEFORE you call
- End_Make_Undoable.
- */
-
- #include <Types.h>
-
- #include "TE32K.h"
-
- #include "UndoRedoSystem.h"
-
- /* Use this if you write a C++ class */
- /*
- TE32KHandle gTextedit;
- */
-
-
- /* This is to be compatible with POV-Ray 3.0.1 for MacOS */
-
- #include "TextEditor.h"
- #define gTextedit gSrcWind_TE32KH
-
-
- /* Variables and structures for the undo system */
-
- My_UndoBuffer_Typedef gUndoBuffer[kMaxUndoRedo];
- long gUndoRedoSelStart;
- long gUndoRedoSelEnd;
- long gUndoRedoTextLength;
- int gUndoBufferCount;
- Boolean gUndoMaking;
- Boolean gNoMonitoring;
-
- /* Variables and structures for the redo system */
-
- My_RedoBuffer_Typedef gRedoBuffer[kMaxUndoRedo];
- int gRedoBufferCount;
- Boolean gRedoMaking;
-
-
- /*
- Use Init_UndoRedoSystem and Dispose_UndoRedoSystem in open and close
- text editor (window) function.
- */
-
- void Init_UndoRedoSystem(void)
- {
- int i;
-
- gUndoBufferCount = 0;
- gRedoBufferCount = 0;
- gUndoMaking = false;
- gRedoMaking = false;
- gNoMonitoring = false;
-
- for(i = 0; i < kMaxUndoRedo; i++)
- {
- gUndoBuffer[i].text = NULL;
- gRedoBuffer[i].text = NULL;
- }
- }
-
- void Dispose_UndoRedoSystem(void)
- {
- Clear_UndoBuffer();
- Clear_RedoBuffer();
- }
-
- /*
- Use Check_CanUndo and Check_CanRedo to enable / disable your menu items.
- Use Get_UndoCommand and Get_RedoCommand to get the command names for the menu items.
- */
-
- Boolean Check_CanUndo(void)
- {
- if((gUndoBufferCount > 0) || (gUndoMaking == true))
- return true;
- return false;
- }
-
- Boolean Check_CanRedo(void)
- {
- if((gRedoBufferCount > 0) || (gRedoMaking == true))
- return true;
- return false;
- }
-
- void Get_UndoCommand(char *str)
- {
- *str = 0;
- if(gUndoBufferCount > 0)
- BlockMove(gUndoBuffer[gUndoBufferCount - 1].command, str, 20);
- }
-
- void Get_RedoCommand(char *str)
- {
- *str = 0;
- if(gRedoBufferCount > 0)
- BlockMove(gRedoBuffer[gRedoBufferCount - 1].command, str, 20);
- }
-
- /*
- Use Begin_UndoRedo_Monitoring and End_UndoRedo_Monitoring for undo correction
- if the executed operation changes text but is NOT undoable (e.g. "Replace All")
- In case of "Replace All" remember to call this for every single "Replace"
- because some monitored text may be between two replaced text parts.
- */
-
- void Begin_UndoRedo_Monitoring(void)
- {
- End_Make_Undoable();
- End_Make_Redoable();
-
- gUndoRedoSelStart = (*gTextedit)->selStart;
- gUndoRedoSelEnd = (*gTextedit)->selEnd;
-
- gUndoRedoTextLength = (*gTextedit)->teLength;
- }
-
- void End_UndoRedo_Monitoring(Boolean change_undo,Boolean change_redo)
- {
- long lendif;
- int i;
-
- if(gNoMonitoring == true)
- return;
-
- lendif = (*gTextedit)->teLength - gUndoRedoTextLength;
-
- if(lendif != 0)
- {
- if(change_undo == true)
- {
- for(i = 0; i < gUndoBufferCount; i++)
- {
- if(gUndoBuffer[i].selStart >= gUndoRedoSelStart)
- gUndoBuffer[i].selStart += lendif;
- if(gUndoBuffer[i].selEnd >= gUndoRedoSelEnd)
- gUndoBuffer[i].selEnd += lendif;
- if(gUndoBuffer[i].selStart < 0)
- gUndoBuffer[i].selStart = 0;
- if(gUndoBuffer[i].selEnd < 0)
- gUndoBuffer[i].selEnd = 0;
- if(gUndoBuffer[i].selStart > (*gTextedit)->teLength)
- gUndoBuffer[i].selStart = (*gTextedit)->teLength;
- if(gUndoBuffer[i].selEnd > (*gTextedit)->teLength)
- gUndoBuffer[i].selEnd = (*gTextedit)->teLength;
- }
- }
- if(change_redo == true)
- {
- for(i = 0; i < gRedoBufferCount; i++)
- {
- if(gRedoBuffer[i].selStart >= gUndoRedoSelStart)
- gRedoBuffer[i].selStart+=lendif;
- if(gRedoBuffer[i].selEnd >= gUndoRedoSelEnd)
- gRedoBuffer[i].selEnd += lendif;
- if(gRedoBuffer[i].selStart < 0)
- gRedoBuffer[i].selStart = 0;
- if(gRedoBuffer[i].selEnd < 0)
- gRedoBuffer[i].selEnd = 0;
- if(gRedoBuffer[i].selStart > (*gTextedit)->teLength)
- gRedoBuffer[i].selStart = (*gTextedit)->teLength;
- if(gRedoBuffer[i].selEnd > (*gTextedit)->teLength)
- gRedoBuffer[i].selEnd = (*gTextedit)->teLength;
- }
- }
- }
- }
-
-
- /*
- Use Begin_Make_Undoable and End_Make_Undoable to make a operation's changes undoable.
-
- NOTE: If the undo buffer is full the last item will be removed automatically.
- Called by My_DoRedo to allow undo for the redo operation.
-
- Parameters:
- cammand - pass a string that can be displayed in addition to the undo menu item.
-
- They will return:
- false if undo is impossible (e.g. out of memory).
- true if undo is possible.
- */
-
- Boolean Begin_Make_Undoable(char *command,Boolean merge)
- {
- Handle fulltext;
- long len,oldlen,addlen;
-
- if(gUndoMaking == true)
- End_Make_Undoable();
-
- Begin_UndoRedo_Monitoring();
-
- gUndoBuffer[gUndoBufferCount - 1].mergeable = merge;
-
- if((merge == true) && (gUndoBufferCount > 0))
- {
- if((strcmp(command,gUndoBuffer[gUndoBufferCount - 1].command) == 0) && (gUndoBuffer[gUndoBufferCount - 1].mergeable == true))
- {
- if((*gTextedit)->selEnd == gUndoBuffer[gUndoBufferCount - 1].selStart)
- {
- gUndoBufferCount--;
- oldlen = GetHandleSize(gUndoBuffer[gUndoBufferCount].text);
- addlen = (*gTextedit)->selEnd - (*gTextedit)->selStart;
- len = oldlen + addlen;
- SetHandleSize(gUndoBuffer[gUndoBufferCount].text, len);
- if(GetHandleSize(gUndoBuffer[gUndoBufferCount].text) != len)
- {
- gUndoBufferCount++;
- return false;
- }
- fulltext = TE32KGetText(gTextedit);
- if(fulltext == NULL)
- {
- gUndoBufferCount++;
- return false;
- }
- HLock(fulltext);
- HLock(gUndoBuffer[gUndoBufferCount].text);
- BlockMove(*(gUndoBuffer[gUndoBufferCount].text), *(gUndoBuffer[gUndoBufferCount].text) + addlen, oldlen);
- BlockMove((*fulltext) + (*gTextedit)->selStart, *(gUndoBuffer[gUndoBufferCount].text), addlen);
- HUnlock(gUndoBuffer[gUndoBufferCount].text);
- HUnlock(fulltext);
- gUndoBuffer[gUndoBufferCount].selStart = (*gTextedit)->selStart;
- }
- else if((*gTextedit)->selStart == gUndoBuffer[gUndoBufferCount - 1].selEnd)
- {
- gUndoBufferCount--;
- oldlen = GetHandleSize(gUndoBuffer[gUndoBufferCount].text);
- addlen = (*gTextedit)->selEnd - (*gTextedit)->selStart;
- len = oldlen + addlen;
- SetHandleSize(gUndoBuffer[gUndoBufferCount].text, len);
- if(GetHandleSize(gUndoBuffer[gUndoBufferCount].text) != len)
- {
- gUndoBufferCount++;
- return false;
- }
- fulltext = TE32KGetText(gTextedit);
- if(fulltext == NULL)
- {
- gUndoBufferCount++;
- return false;
- }
- HLock(fulltext);
- HLock(gUndoBuffer[gUndoBufferCount].text);
- BlockMove((*fulltext) + (*gTextedit)->selStart, *(gUndoBuffer[gUndoBufferCount].text) + oldlen, addlen);
- HUnlock(gUndoBuffer[gUndoBufferCount].text);
- HUnlock(fulltext);
- gUndoBuffer[gUndoBufferCount].selStart = (*gTextedit)->selStart;
- }
- else
- {
- gUndoBuffer[gUndoBufferCount - 1].mergeable = false;
- merge = false;
- }
- }
- else
- {
- gUndoBuffer[gUndoBufferCount - 1].mergeable = false;
- merge = false;
- }
- }
- else
- {
- if(gUndoBufferCount > 0)
- gUndoBuffer[gUndoBufferCount - 1].mergeable = false;
- merge = false;
- }
-
- if(merge == false)
- {
- if(gUndoBufferCount == kMaxUndoRedo)
- {
- if(gUndoBuffer[0].text != NULL)
- DisposeHandle(gUndoBuffer[0].text);
- BlockMove(&gUndoBuffer[1], &gUndoBuffer[0], sizeof(My_UndoBuffer_Typedef) * (kMaxUndoRedo - 1));
- gUndoBufferCount--;
- }
-
- len = (*gTextedit)->selEnd - (*gTextedit)->selStart;
- gUndoBuffer[gUndoBufferCount].text = NewHandle(len);
- if(gUndoBuffer[gUndoBufferCount].text == NULL)
- return false;
-
- fulltext = TE32KGetText(gTextedit);
- if(fulltext == NULL)
- return false;
- HLock(fulltext);
- HLock(gUndoBuffer[gUndoBufferCount].text);
- BlockMove((*fulltext) + (*gTextedit)->selStart, *(gUndoBuffer[gUndoBufferCount].text), len);
- HUnlock(gUndoBuffer[gUndoBufferCount].text);
- HUnlock(fulltext);
-
- gUndoBuffer[gUndoBufferCount].selStart = (*gTextedit)->selStart;
- BlockMove(command, &(gUndoBuffer[gUndoBufferCount].command), 20);
- }
-
- gUndoMaking = true;
-
- return true;
- }
-
- Boolean End_Make_Undoable(void)
- {
- if(gUndoMaking == false)
- return true;
- gUndoMaking = false;
-
- if(gUndoBuffer[gUndoBufferCount].text == NULL)
- return false;
-
- gUndoBuffer[gUndoBufferCount].selEnd = (*gTextedit)->selEnd;
-
- // printf("UNDO Buffered (%d): %d - %d\n",gUndoBufferCount,gUndoBuffer[gUndoBufferCount].selStart,gUndoBuffer[gUndoBufferCount].selEnd);
-
- End_UndoRedo_Monitoring(false,true);
-
- gUndoBufferCount++;
-
- return true;
- }
-
-
- /*
- Use Begin_Make_Redoable and End_Make_Redoable to make an undone operation redoable.
-
- NOTE: If the redo buffer is full the last item will be removed automatically.
- Usually called by My_DoUndo only.
-
- Parameters:
- cammand - pass a string that can be displayed in addition to the undo menu item.
-
- They will return:
- false if redo is impossible (e.g. out of memory).
- true if redo is possible.
- */
-
- Boolean Begin_Make_Redoable(char *command,Boolean merge)
- {
- Handle fulltext;
- long len;
-
- if(gRedoMaking == true)
- End_Make_Redoable();
-
- Begin_UndoRedo_Monitoring();
-
- if(gRedoBufferCount == kMaxUndoRedo)
- {
- if(gRedoBuffer[0].text != NULL)
- DisposeHandle(gRedoBuffer[0].text);
- BlockMove(&gRedoBuffer[1], &gRedoBuffer[0], sizeof(My_RedoBuffer_Typedef) * (kMaxUndoRedo - 1));
- gRedoBufferCount--;
- }
-
- len = (*gTextedit)->selEnd - (*gTextedit)->selStart;
- gRedoBuffer[gRedoBufferCount].text = NewHandle(len);
- if(gRedoBuffer[gRedoBufferCount].text == NULL)
- return false;
-
- fulltext = TE32KGetText(gTextedit);
- if(fulltext == NULL)
- return false;
- HLock(fulltext);
- HLock(gRedoBuffer[gRedoBufferCount].text);
- BlockMove((*fulltext) + (*gTextedit)->selStart, *(gRedoBuffer[gRedoBufferCount].text), len);
- HUnlock(gRedoBuffer[gRedoBufferCount].text);
- HUnlock(fulltext);
-
- gRedoBuffer[gRedoBufferCount].selStart = (*gTextedit)->selStart;
- BlockMove(command, &(gRedoBuffer[gRedoBufferCount].command), 20);
-
- gRedoMaking = true;
-
- return true;
- }
-
- Boolean End_Make_Redoable(void)
- {
- if(gRedoMaking == false)
- return true;
-
- gRedoMaking = false;
-
- if(gRedoBuffer[gRedoBufferCount].text == NULL)
- return false;
-
- gRedoBuffer[gRedoBufferCount].selEnd = (*gTextedit)->selEnd;
-
- // printf("REDO Buffered (%d): %d - %d\n",gRedoBufferCount,gRedoBuffer[gRedoBufferCount].selStart,gRedoBuffer[gRedoBufferCount].selEnd);
-
- End_UndoRedo_Monitoring(true,false);
-
- gRedoBufferCount++;
-
- return true;
- }
-
-
- /*
- Use ClearUndoBuffer and ClearRedoBuffer to delete the undo/redo buffers.
- You have to call them at the end of your program and may have to call them
- if you perform not undoable or not redoable operations.
- */
-
- void Clear_UndoBuffer(void)
- {
- int i;
-
- if(gUndoMaking == true)
- End_Make_Undoable();
-
- for(i = 0; i < gUndoBufferCount; i++)
- {
- if(gUndoBuffer[i].text != NULL)
- DisposeHandle(gUndoBuffer[i].text);
- gUndoBuffer[i].text = NULL;
- }
-
- gUndoBufferCount = 0;
- }
-
- void Clear_RedoBuffer(void)
- {
- int i;
-
- if(gRedoMaking == true)
- End_Make_Redoable();
-
- for(i =0; i < gRedoBufferCount; i++)
- {
- if(gRedoBuffer[i].text != NULL)
- DisposeHandle(gRedoBuffer[i].text);
- gRedoBuffer[i].text = NULL;
- }
-
- gRedoBufferCount = 0;
- }
-
-
- /*
- My_DoUndo and My_DoRedo provide the undo / redo operations.
- */
-
- Boolean My_DoUndo(void)
- {
- if(gUndoMaking == true)
- End_Make_Undoable();
-
- if(gUndoBufferCount > 0)
- {
- // These five lines protect the memory because Monitoring may result in Starts & Ends that are out of range (still investigating cause, March 4th)
- if((gUndoBuffer[gUndoBufferCount - 1].selStart < 0) || (gUndoBuffer[gUndoBufferCount - 1].selEnd > (*gTextedit)->teLength))
- {
- Clear_UndoBuffer();
- return false;
- }
-
- gUndoBufferCount--;
-
- printf("UNDO %s (%d): Current: %d - %d Undone %d - %d\n",
- gUndoBuffer[gUndoBufferCount].command,
- gUndoBufferCount,
- gUndoBuffer[gUndoBufferCount].selStart,
- gUndoBuffer[gUndoBufferCount].selEnd,
- gUndoBuffer[gUndoBufferCount].selStart,
- gUndoBuffer[gUndoBufferCount].selStart +
- GetHandleSize(gUndoBuffer[gUndoBufferCount].text));
-
- if(gUndoBuffer[gUndoBufferCount].text == NULL)
- return false;
-
- TE32KSetSelect(gUndoBuffer[gUndoBufferCount].selStart,gUndoBuffer[gUndoBufferCount].selEnd,gTextedit);
-
- gNoMonitoring = true;
- Begin_Make_Redoable(gUndoBuffer[gUndoBufferCount].command,false);
-
- TE32KDelete(gTextedit);
- TE32KSetSelect(gUndoBuffer[gUndoBufferCount].selStart,
- gUndoBuffer[gUndoBufferCount].selStart,gTextedit);
-
- HLock(gUndoBuffer[gUndoBufferCount].text);
- TE32KInsert(*(gUndoBuffer[gUndoBufferCount].text),GetHandleSize(gUndoBuffer[gUndoBufferCount].text),gTextedit);
- HUnlock(gUndoBuffer[gUndoBufferCount].text);
-
- End_Make_Redoable();
- gNoMonitoring = false;
- TE32KSetSelect(gUndoBuffer[gUndoBufferCount].selStart,gUndoBuffer[gUndoBufferCount].selStart+GetHandleSize(gUndoBuffer[gUndoBufferCount].text),gTextedit);
-
- DisposeHandle(gUndoBuffer[gUndoBufferCount].text);
- gUndoBuffer[gUndoBufferCount].text = NULL;
-
- return true;
- }
-
- return false;
- }
-
- Boolean My_DoRedo(void)
- {
- if(gRedoMaking == true)
- End_Make_Redoable();
-
- if(gRedoBufferCount > 0)
- {
- // These five lines protect the memory because Monitoring may result in Starts & Ends that are out of range (still investigating cause, March 4th)
- if((gRedoBuffer[gRedoBufferCount - 1].selStart < 0) || (gRedoBuffer[gRedoBufferCount - 1].selEnd > (*gTextedit)->teLength))
- {
- Clear_RedoBuffer();
- return false;
- }
-
- gRedoBufferCount--;
-
- printf("REDO %s (%d): Current: %d - %d Redone %d - %d\n",
- gRedoBuffer[gRedoBufferCount].command,
- gRedoBufferCount,
- gRedoBuffer[gRedoBufferCount].selStart,
- gRedoBuffer[gRedoBufferCount].selEnd,
- gRedoBuffer[gRedoBufferCount].selStart,
- gRedoBuffer[gRedoBufferCount].selStart +
- GetHandleSize(gRedoBuffer[gRedoBufferCount].text));
-
- if(gRedoBuffer[gRedoBufferCount].text == NULL)
- return false;
-
- TE32KSetSelect(gRedoBuffer[gRedoBufferCount].selStart,gRedoBuffer[gRedoBufferCount].selEnd,gTextedit);
-
- gNoMonitoring = true;
- Begin_Make_Undoable(gRedoBuffer[gRedoBufferCount].command,false);
-
- TE32KDelete(gTextedit);
- TE32KSetSelect(gRedoBuffer[gRedoBufferCount].selStart,gRedoBuffer[gRedoBufferCount].selStart,gTextedit);
-
- HLock(gRedoBuffer[gRedoBufferCount].text);
- TE32KInsert(*(gRedoBuffer[gRedoBufferCount].text),GetHandleSize(gRedoBuffer[gRedoBufferCount].text),gTextedit);
- HUnlock(gRedoBuffer[gRedoBufferCount].text);
-
- End_Make_Undoable();
- gNoMonitoring = false;
- TE32KSetSelect(gRedoBuffer[gRedoBufferCount].selStart,gRedoBuffer[gRedoBufferCount].selStart+GetHandleSize(gRedoBuffer[gRedoBufferCount].text),gTextedit);
-
- DisposeHandle(gRedoBuffer[gRedoBufferCount].text);
- gRedoBuffer[gRedoBufferCount].text = NULL;
-
- return true;
- }
-
- return false;
- }
-